#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2008  Vladimir Lagunov

import gc, xmpp, os, sys, traceback, stats, timer, vcard
from random import random
from time import sleep, time, localtime
from config import *
import logging

try:
	import psyco
	psyco.full()
except ImportError:
	pass

gc.enable()
gc.set_debug(0)

class bot:
	def __init__(self, basename):
		self.f = open(basename,'r')
		self.is_right = True
		self.question = []
		self.hints_letters = []
		self.hints_count = 0
		self.game_started = False
		self.new_quest_demand = None
		self.change_game_demand = None
		self.change_game_timer = timer.Timer(60)
		self.tm = timer.Timer(question_period)
		self.status_change = True
		self.silence_quest_count = 0
		self.logs = logging.Logger(room)
		try:
			self.connect_to_server()
			self.work()
		except KeyboardInterrupt:
			pass
		except:
			errorfile = open('error.log', 'w')
			traceback.print_exc(file=errorfile)
			errorfile.close()
			if not enable_bug_report:
				raise
			errorfile = open('error.log', 'r')
			self.cl.send(xmpp.protocol.Message(to=bug_report_jid, body=errorfile.read(), subject='Traceback'))
			errorfile.close()
			raise
		
	# Get new question from file
	def get_quest(self):
		for i in xrange(int(random() * max_shuffle)):
			self.f.readline()
		quest = self.f.readline()
		if quest == '':
			self.f.seek(0)
			return self.get_quest()
		sp = quest.index('|')
		if codepage.upper() == 'UTF-8':
			offset = -2
		else:
			offset = -1
		x = [
			unicode(quest[:sp], codepage),
			unicode(quest[sp+1:offset], codepage) # because #13#10
		] 
		return x


	# Generate iq for responsing client's version
	def iq_os_version(self, conn, iq):
		if iq.getQueryNS() == 'jabber:iq:version':
			msg = xmpp.protocol.Iq(typ='result', to=iq.getFrom(), frm=myjid+'/'+bot_resource)
			msg.setAttr('xml:lang','ru-RU')
			msg.setQueryNS('jabber:iq:version')
			query = msg.getChildren()[0]
			query.addChild('name').setData('Python '+'.'.join([str(x) for x in sys.version_info[:3]])+', Erudit')
			query.addChild('version').setData(version)
			if os.name in ('dos', 'nt'):
				query.addChild('os').setData(sys.winver)
			elif os.name == 'posix':
				query.addChild('os').setData(os.uname()[0]+' '+os.uname()[2])
			else:
				query.addChild('os').setData('Unknown')
			self.cl.send(msg)
			raise xmpp.NodeProcessed
		elif iq.getQueryNS() == 'jabber:iq:last':
			msg = xmpp.protocol.Iq(typ='result', to=iq.getFrom(), frm=myjid+'/'+bot_resource, queryNS='jabber:iq:last')
			msg.setAttr('xml:lang','ru-RU')
			msg.getChildren()[0].setAttr('seconds', '2')
			self.cl.send(msg)
			raise xmpp.NodeProcessed

	# Generate russian ending for some number
	# 0 - очки, 1 - буквы, 2 - секунды
	def get_ending(self,i,typ=0):
		if typ == 0:
			no, n1, n234, nm = u'ов', u'о', u'а', u'ов'
		elif typ == 1:
			no, n1, n234, nm = u'', u'а', u'ы', u''
		elif typ == 2:
			no, n1, n234, nm = u'', u'а', u'ы', u''
		if i % 100 in xrange(11, 20):
			return no
		elif i % 10 == 1:
			return n1
		elif i % 10 in (2, 3, 4):
			return n234
		else:
			return nm

	# Send message to room. "*" toggles bold.
	def send_msg(self, msg, bold=False):
		body = reduce(lambda x,y: x+y, [x for x in msg if x != '*'])
		tag = xmpp.protocol.Message(room, body, 'groupchat')
		if bold:
			xhtmltag = tag.addChild('html', namespace='http://jabber.org/protocol/xhtml-im'). \
			    addChild('body', namespace='http://www.w3.org/1999/xhtml'). \
			    addChild('span')
			xhtmltag.setAttr('style', boldstyle)
			xhtmltag.setData(msg.replace(u'  ', u'  '))
		self.cl.send(tag)

	# Start game
	def process_start(self, member, mystr):
		if self.game_started:
			return
		if self.change_game_demand and member != self.change_game_demand:
			self.send_msg(u'Игра началась!', True)
			#self.question = []
			sleep(sleep_before_next_message)
			self.game_started = True
			self.is_right = True
			self.hints_letters = []
			self.hints_count = 0
			self.tm.flush()
			self.new_quest_demand = None
			self.change_game_demand = None
		else:
			self.change_game_demand = member
			self.change_game_timer.flush()
		self.status_change = True

	# Stop game
	def process_stop(self, member, mystr):
		if not self.game_started:
			return
		if member != self.change_game_demand and self.change_game_demand != None:
			self.send_msg(u'Игра приостановлена!', True)
			self.game_started = False
			self.change_game_demand = None
			self.status_change = True
		elif self.change_game_demand == None:
			self.change_game_demand = member
			self.change_game_timer.flush()

	# Force changing question
	def process_newquestion(self, member, mystr):
		if self.game_started:
			if not self.new_quest_demand:
				self.new_quest_demand = member
			elif self.new_quest_demand != member:
				self.is_right = True
				self.hints_letters = []
				self.hints_count = 0
				self.tm.flush()
				self.new_squest_demand = None
				self.send_msg(u'Вопрос пропущен.', True)
				sleep(sleep_before_next_message)

	# Sending hint
	def process_hint(self, member, mystr):
		if self.question == []:
			return
		msg = u'Подсказка: '
		if len(self.question[1]) < 3:
			self.send_msg(u'Нет уж, не буду ничего вам подсказывать.')
			return
		if len(self.question[1]) - self.hints_count > 2:
			letter_index = int(random() * len(self.question[1]))
			while letter_index in self.hints_letters or \
				    self.question[1][letter_index] == ' ':
				letter_index += 1
				if letter_index == len(self.question[1]):
					letter_index = 0
			self.hints_count += 1
			self.hints_letters.append(letter_index)
			msg = u''
			for i in xrange(len(self.question[1])):
				if i in self.hints_letters:
					msg = "%s%s " % (msg, self.question[1][i])
				elif self.question[1][i] ==' ':
					msg = "%s  " % msg
				else:
					msg = "%s_ " % msg
		else:
			self.send_msg(u'Все, хватит с вас подсказок.')
			return
		# if self.hints_letter == -1:
# 			self.hints_letter = int(random() * len(self.question[1]))
# 			for i in xrange(self.hints_letter):
# 				if self.question[1][i] != ' ':
# 					msg += u'_ '
# 				else:
# 					msg += u'  '
# 			msg += self.question[1][self.hints_letter]
# 			for i in xrange(self.hints_letter+1, len(self.question[1])):
# 				if self.question[1][i] != ' ':
# 					msg += u' _'
# 				else:
# 					msg += u'  '
# 		elif self.hints_letter != -2:
# 			new_letter = int(random() * len(self.question[1]))
# 			if new_letter == self.hints_letter:
# 				if new_letter != len(self.question[1])-1:
# 					new_letter += 1
# 				else:
# 					new_letter = 0
# 			if new_letter > self.hints_letter:
# 				new_letter, self.hints_letter = self.hints_letter, new_letter
# 			for i in xrange(new_letter):
# 				if self.question[1][i] != ' ':
# 					msg += u'_ '
# 				else:
# 					msg += u'  '
# 			msg += self.question[1][new_letter] + u' '
# 			for i in xrange(new_letter+1, self.hints_letter):
# 				if self.question[1][i] != ' ':
# 					msg += u'_ '
# 				else:
# 					msg += u'  '
# 			msg += self.question[1][self.hints_letter]
# 			for i in xrange(self.hints_letter+1, len(self.question[1])):
# 				if self.question[1][i] != ' ':
# 					msg += u' _'
# 				else:
# 					msg += u'  '
# 			self.hints_letter = -2
# 		else:
# 			msg = u'Все, хватит с вас подсказок.'
# 			self.send_msg(msg)
# 			return
		if stats.findUser(member) == -1:
			stats.addUser(member)
		#stats.addScore(member, -hint_fine)
		#msg += u'. С ' + member + u' снято ' + unicode(hint_fine) + u' очков.'
		self.quest_coast -= hint_fine
		msg += u'. Теперь за вопрос дается ' + unicode(self.quest_coast) + u' очков.'
		self.send_msg(msg, True)

	# Checking answer for question
	def process_quest(self, member, mystr):
		if member == '' or mystr == None or self.question == []:
			return
		if stats.findUser(member) == -1:
			stats.addUser(member)
		if self.question[1].upper() == mystr.upper():
			quest_time = int(self.tm.count())
			self.send_msg(member + u' да, вы угадали! Вы получаете ' + \
					      unicode(self.quest_coast)+u' очков.\nПравильный ответ был дан за ' + \
					      unicode(quest_time)+u' секунд'+self.get_ending(quest_time, 2)+u'.', True)
			sleep(sleep_before_next_message)
			self.is_right = True
			self.tm.flush()
			self.hints_letters = []
			self.hints_count = 0
			stats.addScore(member, self.quest_coast)
			stats.updateBase()

	# Getting statistics of player(s)
	def process_statistics(self, member, mystr):
		tmpstr = mystr.split(' ')
		mystr = []
		for i in tmpstr:
			if i != '':
				mystr.append(i)
		del tmpstr
		msglen = len(stats.UserBase)
		if msglen == 0:
			self.send_msg(u'Статистика пуста.')
			return
		if len(mystr) == 1:
			if msglen > max_statistics:
				msglen = max_statistics
			msg = u'Список '+unicode(max_statistics)+u' лучших игроков:\n'
			copydict = stats.sortByScore()
			for i in xrange(1, msglen+1):
				j = copydict[-i]
				msg += unicode(i) + u'. ' + j[0] + u' набрал ' + \
				    unicode(j[1]) + u' очк' + self.get_ending(j[1]) + u'\n'
		else:
			j = stats.findUser(mystr[1])
			if j == -1:
				msg = u'Пользователя ' + mystr[1] + u' в статистике нет.'
			else:
				jj = stats.UserBase[j][1]
				msg = mystr[1] + u' набрал ' + unicode(jj) + u' очк' + self.get_ending(jj) + u'.\n'
		self.send_msg(msg)

	# Getting statistics for some user
	def process_mystatistics(self, member, mystr):
		i = stats.findUser(member)
		if i == -1:
			stats.addUser(member)
			msg = member + u', вы набрали 0 очков.'
		else:
			j = stats.UserBase[i][1]
			msg = member + u', вы набрали ' + unicode(j) + u' очк' + self.get_ending(j) + u'.'
		self.send_msg(msg)

	# Authorizing everyone who adds bot into roster
	def authRoster(self, conn, msg):
		r = self.cl.getRoster()
		x = msg.getFrom()
		r.Authorize(x)
		r.Subscribe(x)

	# Processing 'message' stanza
	def message_process(self, conn, msg):
		for i in msg.getChildren():
			if i.getNamespace() == 'jabber:x:delay':
				return
		mystr = msg.getBody()
		if mystr == None:
			return
		if msg.getType() == 'groupchat':
			self.logs.writeMessage(msg.getFrom(), msg.getBody())
			member = msg.getFrom().resource
			if member != bot_resource:
				self.silence_quest_count = 0
				if mystr in start_command:
					self.process_start(member, mystr)
				elif mystr in stop_command:
					self.process_stop(member, mystr)
				elif mystr in statistics_command:
					self.process_statistics(member, mystr)
				elif mystr in mystatistics_command:
					self.process_mystatistics(member, mystr)
				elif mystr in nextq_command:
					self.process_newquestion(member, mystr)
				if self.game_started:
					if mystr in hint_command:
						self.process_hint(member, mystr)
					else:
						self.process_quest(member, mystr)

	# Do nothing
	def skip_last_msgs(self, msg):
		pass

	def connect_to_server(self):
		jid = xmpp.protocol.JID(myjid)
		self.cl = xmpp.Client(jid.getDomain(), debug=[])
		self.cl.connect()
		self.cl.auth(jid.getNode(), pwd, resource=bot_resource)
		self.cl.send(vcard.generateVCard())
		self.cl.sendInitPresence()
		self.cl.RegisterHandler('message', self.message_process)
		self.cl.RegisterHandler('iq', self.iq_os_version)
		self.cl.RegisterHandler('presence', self.authRoster, typ='subscribe')
		self.cl.RegisterHandler('presence', self.do_nothing, typ='unavaliable')
		self.cl.send(xmpp.Presence(to=room+'/'+bot_resource))
	
	def do_nothing(conn, msg):
		pass

	def work(self):
		i = 1
		while True:
			if self.game_started:
				if self.is_right:
					self.question = []
					#self.cl.Process(sleep_before_next_message)
					self.question = self.get_quest() 
					self.new_quest_demand = None
					self.is_right = False
					self.quest_coast = 1
					quest_words = self.question[1].split(' ')
					msg = u'А теперь вопрос:\n' + self.question[0] + u' ('
					if len(quest_words) == 1:
						msg += unicode(len(self.question[1]))+u' букв'+ \
						    self.get_ending(len(self.question[1]), 1)+')\n'
					else:
						msg += u', '.join(unicode(len(x)) for x in quest_words[:-1])
						msg += u' и ' + unicode(len(quest_words[-1])) + u' букв)\n'   
					self.send_msg(msg, True)
					for x in self.cl.getRoster().getRawRoster()[room]['resources']:
						if self.game_started:
							msg = self.question[0]
						else:
							msg = 'Игра приостановлена.'
						pres = xmpp.protocol.Presence(status=self.question[0], to=room+'/'+x)
						self.cl.send(pres)
				self.cl.Process(1)
				if self.change_game_demand and self.change_game_timer.is_end():
					self.change_game_demand = None
					self.change_game_timer.flush()
				if self.tm.is_end() and not self.is_right:
					self.send_msg(u'Увы, никто не сказал правильного ответа. Это - '+self.question[1]+u'.', True)
					self.silence_quest_count += 1
					self.tm.flush()
					self.is_right = True
					self.hints_letter = []
					self.hints_count = 0
				if self.silence_quest_count > max_silence_quest:
					self.send_msg(u'Игра приостановлена!', True)
					self.game_started = False
					self.change_game_demand = None
					self.status_change = True
				self.cl.Process(1)
			else:
				self.cl.Process(5)
				if self.change_game_demand and self.change_game_timer.is_end():
					self.change_game_demand = None
					self.change_game_timer.flush()
				if self.status_change:
					if self.game_started:
						msg = u'Игра началась!'
					else:
						msg = u'Игра приостановлена.'
					pres = xmpp.protocol.Presence(priority='50', status=msg)
					self.cl.Process(1)
					self.cl.send(pres)
					self.question = []
					#self.cl.Process(sleep_before_next_message)
					self.status_change = False

if __name__ == '__main__':
	bot(questfile)
